package com.john.bms.realm;

import com.john.bms.mapper.system.RoleMapper;
import com.john.bms.mapper.system.UserMapper;
import com.john.bms.model.system.role.SysRole;
import com.john.bms.model.system.user.SysUser;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

import javax.annotation.Resource;
import java.util.List;

/**
 * @description: 在Shiro中，最终是通过Realm来获取应用程序中的用户、角色及权限信息的
 * @ auth:张家小帅哥
 */
public class MyShiroRealm extends AuthorizingRealm {

    @Resource
    private UserMapper userMapper;

    @Resource
    private RoleMapper roleMapper;

    /**
     * @param token
     * @return
     * @throws AuthenticationException
     * @desc:验证用户身份
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        //加这一步的目的是在Post请求的时候会先进认证，然后在到请求
        if (token.getPrincipal() == null) {
            return null;
        }

        //UsernamePasswordToken用于存放提交的登录信息
        //UsernamePasswordToken token = (UsernamePasswordToken)token;
        //从token里取出用户信息
        String username = (String) token.getPrincipal();
        String password = new String((char[]) token.getCredentials());

        //从数据库查询用户信息
        SysUser user = userMapper.doLogin(username, password);
        //用户账号状态验证,或者调用 CredentialsMatcher 校验
        if (user == null) {
            throw new UnknownAccountException();
        }
//        if (!password.equals(user.getPassword())) {
//            throw new IncorrectCredentialsException();
//        }
        if (0 == user.getState()) {
            throw new DisabledAccountException();
        }
        if (2 == user.getState()) {
            throw new LockedAccountException();
        }
        //IncorrectCredentialsException不正确的凭证
        // ExpiredCredentialsException 凭证过期
        //调用 CredentialsMatcher 校验 还需要创建一个类 继承CredentialsMatcher  如果在上面校验了,这个就不需要了
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(), getName());
        return simpleAuthenticationInfo;
    }

    /**
     * 授权用户权限
     * 授权的方法是在碰到<shiro:hasPermission name=''></shiro:hasPermission>标签的时候调用的
     * 它会去检测shiro框架中的权限(这里的permissions)是否包含有该标签的name值,如果有,里面的内容显示
     * 如果没有,里面的内容不予显示(这就完成了对于权限的认证.)
     * <p>
     * shiro的权限授权是通过继承AuthorizingRealm抽象类，重载doGetAuthorizationInfo();
     * 当访问到页面的时候，链接配置了相应的权限或者shiro标签才会执行此方法否则不会执行
     * 所以如果只是简单的身份认证没有权限的控制的话，那么这个方法可以不进行实现，直接返回null即可。
     * <p>
     * 在这个方法中主要是使用类：SimpleAuthorizationInfo 进行角色的添加和权限的添加。
     * authorizationInfo.addRole(role.getRole()); authorizationInfo.addStringPermission(p.getPermission());
     * <p>
     * 当然也可以添加set集合：roles是从数据库查询的当前用户的角色，stringPermissions是从数据库查询的当前用户对应的权限
     * authorizationInfo.setRoles(roles); authorizationInfo.setStringPermissions(stringPermissions);
     * <p>
     * 就是说如果在shiro配置文件中添加了filterChainDefinitionMap.put("/add", "perms[权限添加]");
     * 就说明访问/add这个链接必须要有“权限添加”这个权限才可以访问
     * <p>
     * 如果在shiro配置文件中添加了filterChainDefinitionMap.put("/add", "roles[100002]，perms[权限添加]");
     * 就说明访问/add这个链接必须要有 "权限添加" 这个权限和具有 "100002" 这个角色才可以访问
     *
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("执行了授权");
        //获取用户
        SysUser user = (SysUser) SecurityUtils.getSubject().getPrincipal();
        List<SysRole> sysRoles = roleMapper.selectRoleListByUserId(user.getId());
        //添加角色
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        for (SysRole sysRole : sysRoles) {
            authorizationInfo.addRole(sysRole.getRoleName());
        }

        //添加权限
        authorizationInfo.addStringPermission("role:view");
        authorizationInfo.addStringPermission("user:view");
        authorizationInfo.addStringPermission("menu:view");
        authorizationInfo.addStringPermission("menu:delete");
        authorizationInfo.addStringPermission("menu:add");
        authorizationInfo.addStringPermission("menu:update");
        authorizationInfo.addStringPermission("dictionaries:view");

        return authorizationInfo;
    }

}
